#include "Browser.h"

//#define BROWSER_DEBUG 1

typedef HRESULT (WINAPI *PCoInternetSetFeatureEnabled)(int, DWORD, BOOL);
#define FEATURE_DISABLE_NAVIGATION_SOUNDS 21
#define SET_FEATURE_ON_PROCESS 0x00000002

#define CHK(hr) if(FAILED(hr)) printf("%s (%d): 0x%08x\n", __FILE__, __LINE__, hr);

Browser::Browser(QWidget * parent, const char * name)
	: QWidget(parent, name)
{
	setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

	HRESULT hr = S_OK;

	HWND hWnd = winId();

	RECT rc;
	GetClientRect(hWnd, &rc);

	axWindow.Create(hWnd, rc, 0, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN);

	CComPtr<IUnknown> punkCtrl;
	CHK(axWindow.CreateControlEx(L"Shell.Explorer", NULL, NULL, &punkCtrl));

	CHK(punkCtrl->QueryInterface(IID_IWebBrowser2, (void **)&spWebBrowser2));

	CComPtr<IConnectionPointContainer> spCPC;
	CHK(spWebBrowser2->QueryInterface(IID_IConnectionPointContainer, (void **)&spCPC));

	CHK(spCPC->FindConnectionPoint(__uuidof(DWebBrowserEvents2), &spConnectionPoint));

	CComPtr<IUnknown> spSinkUnk;
	CHK(this->QueryInterface(IID_IUnknown, (void **)&spSinkUnk));
	CHK(spConnectionPoint->Advise(spSinkUnk, &dwCookie));


	HMODULE urlmon = LoadLibrary(L"urlmon.dll");
	if( urlmon ){
		PCoInternetSetFeatureEnabled CoInternetSetFeatureEnabled = NULL;
		CoInternetSetFeatureEnabled = (PCoInternetSetFeatureEnabled)GetProcAddress( urlmon, "CoInternetSetFeatureEnabled" );
		if( CoInternetSetFeatureEnabled ) {
			CoInternetSetFeatureEnabled( FEATURE_DISABLE_NAVIGATION_SOUNDS, SET_FEATURE_ON_PROCESS, TRUE );
		}
		FreeLibrary( urlmon );
	}


	CHK(spWebBrowser2->QueryInterface(IID_IOleObject, (void **)&spClientSite));
	CHK(spWebBrowser2->QueryInterface(IID_IOleObject, (void **)&spControl));

	setFocus();
}

Browser::~Browser()
{
#ifndef BROWSER_DEBUG
	qDebug("~Browser()");
#endif
	CHK(spConnectionPoint->Unadvise(dwCookie));
}

BSTR Browser::QStringToBSTR(const QString &str)
{
   	return SysAllocStringLen((OLECHAR*)str.unicode(), str.length());
}

void Browser::goTo(const QString & addr)
{
	ASSERT(spWebBrowser2 != NULL);

#ifndef BROWSER_DEBUG
	qDebug(QString("%1: %2").arg(__FUNCTION__).arg(addr));
#endif

	CComVariant v;
	CHK(spWebBrowser2->Navigate(QStringToBSTR(addr), &v, &v, &v, &v));
}

void Browser::execJS(const QString & s)
{
	ASSERT(spWebBrowser2 != NULL);

	if (spHTMLWindow2 == NULL)
		return;

	CComVariant v;
	CHK(spHTMLWindow2->execScript(QStringToBSTR(s), CComBSTR("javascript"), &v));
}


void Browser::resizeEvent(QResizeEvent * e)
{	
	QSize s = e->size();
	axWindow.MoveWindow(0, 0, s.width(), s.height());
}

STDMETHODIMP Browser::QueryInterface(REFIID iid, void **ppvObject)
{
	if (iid == __uuidof(DWebBrowserEvents2) || iid == IID_IUnknown || iid == IID_IDispatch)
	{
		*ppvObject = dynamic_cast<IDispatch *>(this);
		return S_OK;
	}

	if (iid == IID_IDocHostUIHandler)
	{
		*ppvObject = dynamic_cast<IDocHostUIHandler *>(this);
		return S_OK;
	}

	return E_NOINTERFACE;
}

#define DISPID_action 12345

STDMETHODIMP Browser::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, 
	VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
{
	if (dispIdMember == DISPID_action)
    {
		if (wFlags & DISPATCH_PROPERTYGET)
		{
			if (pVarResult != NULL)
			{
				
				VariantInit(pVarResult);
				V_VT(pVarResult)=VT_BOOL;
				V_BOOL(pVarResult) = true;
			}
		}
		
		if (wFlags & DISPATCH_METHOD)
		{
			QString s((QChar*)(pDispParams->rgvarg[0].bstrVal), wcslen(pDispParams->rgvarg[0].bstrVal));
		
			QStringList sl = QStringList::split("|", s);

			qDebug(QString("%1(%2)")
				.arg(sl[0])
				.arg(sl.count() > 1 ? sl[1] : "")
			);

			emit action(sl[0], sl.count() > 1 ? sl[1] : "");
		}
    }
	else 	
	if (DISPID_BEFORENAVIGATE2 == dispIdMember)
	{
		VARIANT * var = pDispParams->rgvarg[5].pvarVal;
		QString url((QChar*)var->bstrVal, wcslen(var->bstrVal));

		bool cancel = false;
		emit beforeNavigate(url, &cancel);

		if (cancel)
		{
			VARIANT_BOOL * pbCancel = pDispParams->rgvarg[0].pboolVal;
			*pbCancel = VARIANT_TRUE;
		}
	}
	else
	if (DISPID_NAVIGATECOMPLETE2 == dispIdMember)
	{
		CComPtr<IDispatch> spDocument;
		CHK(spWebBrowser2->get_Document(&spDocument));

/////
		CComPtr<IHTMLDocument2> spDocument2;
		CHK(spDocument->QueryInterface(IID_IHTMLDocument2, (void **)&spDocument2));

//		CComPtr<IHTMLWindow2> spHTMLWindow2;
		CHK(spDocument2->get_parentWindow(&spHTMLWindow2));
/////

		CComPtr<ICustomDoc> spCustomDoc;
		CHK(spDocument->QueryInterface(IID_ICustomDoc, (void **)&spCustomDoc));

		CHK(spCustomDoc->SetUIHandler(this));

		emit navigateComplete2();
	}

	return S_OK;
}

STDMETHODIMP Browser::GetExternal(IDispatch ** ppDispatch)
{
	*ppDispatch = dynamic_cast<IDispatch *>(this);
    return S_OK;
}

STDMETHODIMP Browser::GetIDsOfNames(
			/* [in] */ REFIID riid,
            /* [size_is][in] */ OLECHAR** rgszNames,
            /* [in] */ UINT cNames,
            /* [in] */ LCID lcid,
            /* [size_is][out] */ DISPID* rgDispId)
{
	HRESULT hr = NOERROR;

	for (int i = 0; i < cNames; i++)
	{
		QString s = QString::fromUcs2(rgszNames[0]);
		if (s == "action")
		{
			rgDispId[0] = DISPID_action;
		}
		else
		{
			hr = ResultFromScode(DISP_E_UNKNOWNNAME);
			rgDispId[i] = DISPID_UNKNOWN;
		}
	}

	return hr;
}

void Browser::setFocus()
{
	RECT rcBody;
	CHK(spControl->DoVerb(OLEIVERB_UIACTIVATE, NULL, spClientSite, 0, winId(), &rcBody));
}
